#include "RedisMgr.hpp"
#include "RedisConpool.hpp"
#include "iostream"
#include <cstddef>
#include <cstring>
#include <hiredis/hiredis.h>
#include <hiredis/read.h>
#include <memory>
#include <utility>
RedisMgr::RedisMgr() : _pool(std::make_unique<RedisConPool>()) {}

bool RedisMgr::Get(const std::string &key, std::string &value) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    std::cout << "connect is null." << std::endl;
    return false;
  }
  auto reply =
      (redisReply *)redisCommand(_connect.get(), "GET %s", key.c_str());
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ GET " << key << " ] execute failed!"
              << std::endl;
    return false;
  }
  if (reply->type != REDIS_REPLY_STRING) {
    std::cout << "[ GET " << key << " ] failed" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  value = reply->str;
  freeReplyObject(reply);
  _pool->ReturnConnection(std::move(_connect));
  return true;
}

bool RedisMgr::Set(const std::string &key, std::string &value) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    return false;
  }
  auto reply = (redisReply *)redisCommand(_connect.get(), "SET %s %s",
                                          key.c_str(), value.c_str());
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ SET " << key << " " << value
              << " ] execute failed!" << std::endl;
    return false;
  }
  if (reply->type != REDIS_REPLY_STATUS) {
    std::cout << "[ SET " << key << " " << value << " ] failed" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  if (!(strcmp(reply->str, "ok") == 0 || strcmp(reply->str, "OK") == 0)) {
    std::cout << "[ SET " << key << " " << value << " ] failed" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  freeReplyObject(reply);
  _pool->ReturnConnection(std::move(_connect));
  return true;
}

bool RedisMgr::LPush(const std::string &key, const std::string &value) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    return false;
  }
  auto reply = (redisReply *)redisCommand(_connect.get(), "LPUSH %s %s",
                                          key.c_str(), value.c_str());
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ LPUSH " << key << " " << value
              << " ] execute failed!" << std::endl;
    return false;
  }
  if (reply->type != REDIS_REPLY_INTEGER) {
    std::cout << "Execut command [ LPUSH " << key << " " << value
              << " ] failure!" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  if (reply->integer <= 0) {
    std::cout << "Execut command [ LPUSH " << key << " " << value
              << " ] failure!" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  std::cout << "Execut command [ LPUSH " << key << " " << value << " ] success!"
            << std::endl;
  freeReplyObject(reply);
  _pool->ReturnConnection(std::move(_connect));
  return true;
}

bool RedisMgr::LPop(const std::string &key, std::string &value) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    return false;
  }
  auto reply =
      (redisReply *)redisCommand(_connect.get(), "LPOP %s", key.c_str());

  if (reply == nullptr) {
    std::cerr << "reply is null, command [ LPOP " << key << " " << value
              << " ] execute failed!" << std::endl;
    return false;
  }
  if (reply->type == REDIS_REPLY_NIL) {
    std::cout << "Execut command [ LPOP " << key << " ] failure!" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  value = reply->str;
  std::cout << "Execut command [ LPOP " << key << ":" << value << "] success!"
            << std::endl;
  freeReplyObject(reply);
  _pool->ReturnConnection(std::move(_connect));
  return true;
}

bool RedisMgr::RPush(const std::string &key, const std::string &value) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    return false;
  }
  auto reply = (redisReply *)redisCommand(_connect.get(), "RPUSH %s %s",
                                          key.c_str(), value.c_str());
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ RPUSH " << key << " " << value
              << " ] execute failed!" << std::endl;
    return false;
  }
  if (reply->type != REDIS_REPLY_INTEGER) {
    std::cout << "Execut command [ RPUSH " << key << ":" << value
              << " ] failure!" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  if (reply->integer <= 0) {
    std::cout << "Execut command [ RPUSH " << key << ":" << value
              << " ] failure!" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  std::cout << "Execut command [ RPUSH " << key << ":" << value << " ] success!"
            << std::endl;
  freeReplyObject(reply);
  _pool->ReturnConnection(std::move(_connect));
  return true;
}

bool RedisMgr::RPop(const std::string &key, std::string &value) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    return false;
  }
  auto reply =
      (redisReply *)redisCommand(_connect.get(), "RPOP %s", key.c_str());
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ RPOP " << key << " " << value
              << " ] execute failed!" << std::endl;
    return false;
  }
  if (reply->type == REDIS_REPLY_NIL) {
    std::cout << "Execut command [ RPOP " << key << " ] failure!" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  value = reply->str;
  std::cout << "Execut command [ RPOP " << key << ":" << value << "] success!"
            << std::endl;
  freeReplyObject(reply);
  _pool->ReturnConnection(std::move(_connect));
  return true;
}

bool RedisMgr::HSet(const std::string &key, std::string &hkey,
                    const std::string &value) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    return false;
  }
  auto reply =
      (redisReply *)redisCommand(_connect.get(), "HSET %s %s %s", key.c_str(),
                                 hkey.c_str(), value.c_str());
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ HSET " << key << " " << hkey << " "
              << value << " ] execute failed!" << std::endl;
    return false;
  }
  if (reply->type != REDIS_REPLY_INTEGER) {
    std::cout << "Execut command [ HSET " << key << hkey << value
              << " ] failure!" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  std::cout << "Execut command [ HSET " << key << hkey << value << " ] success!"
            << std::endl;
  freeReplyObject(reply);
  _pool->ReturnConnection(std::move(_connect));
  return true;
}

bool RedisMgr::HSet(const char *key, const char *hkey, const char *hvalue,
                    size_t hvaluelen) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    return false;
  }
  const char *argv[4];
  size_t argvlen[4];
  argv[0] = "HSET";
  argvlen[0] = 4;
  argv[1] = key;
  argvlen[1] = strlen(key);
  argv[2] = hkey;
  argvlen[2] = strlen(hkey);
  argv[3] = hvalue;
  argvlen[3] = strlen(hvalue);
  auto reply = (redisReply *)redisCommandArgv(_connect.get(), 4, argv, argvlen);
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ HSET " << key << " " << hkey << " "
              << hvalue << " ] execute failed!" << std::endl;
    return false;
  }
  if (reply->type != REDIS_REPLY_INTEGER) {
    std::cout << "Execut command [ HSET " << key << hkey << hvalue
              << " ] failure!" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  std::cout << "Execut command [ HSET " << key << hkey << hvalue
            << " ] success!" << std::endl;
  freeReplyObject(reply);
  _pool->ReturnConnection(std::move(_connect));
  return true;
}

bool RedisMgr::HDel(const std::string &key, const std::string &filed) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    return false;
  }
  auto reply = (redisReply *)redisCommand(_connect.get(), "HDEL %s %s",
                                          key.c_str(), filed.c_str());
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ HDEL " << key << " " << filed
              << " ] execute failed!" << std::endl;
    return false;
  }
  bool success = false;
  if (reply->type == REDIS_REPLY_INTEGER) {
    success = reply->integer > 0;
  }
  _pool->ReturnConnection(std::move(_connect));
  freeReplyObject(reply);
  return success;
}

std::string RedisMgr::HGet(const std::string &key, const std::string &hkey) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    std::cout << "connection is null!" << std::endl;
    return "";
  }
  const char *argv[3];
  size_t argvlen[3];
  argv[0] = "HGET";
  argvlen[0] = 4;
  argv[1] = key.c_str();
  argvlen[1] = key.length();
  argv[2] = hkey.c_str();
  argvlen[2] = hkey.length();
  auto reply = (redisReply *)redisCommandArgv(_connect.get(), 3, argv, argvlen);
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ HSET " << key << " " << hkey << " "
              << " ] execute failed!" << std::endl;
    return "";
  }
  // 返回类型nil
  if (reply->type == REDIS_REPLY_NIL) {
    std::cout << "Execut command [ HGET " << key << " " << hkey << " ] failure!"
              << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return "";
  }
  std::string value = reply->str;
  _pool->ReturnConnection(std::move(_connect));
  freeReplyObject(reply);
  std::cout << "Execut command [ HGET " << key << " " << hkey << " " << value
            << " ] success!" << std::endl;
  return value;
}

bool RedisMgr::Del(const std::string &key) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    return false;
  }
  auto reply =
      (redisReply *)redisCommand(_connect.get(), "DEL %s", key.c_str());
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ DEL " << key << " ] execute failed!"
              << std::endl;
    return "";
  }
  if (reply->type != REDIS_REPLY_INTEGER) {
    std::cout << "Execut command [ DEL " << key << " ] failure!" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  if (reply->integer == 0) {
    std::cout << "Execut command [ DEL " << key << " ] failure!" << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  std::cout << "Execut command [ DEL " << key << " ] success!" << std::endl;
  freeReplyObject(reply);
  _pool->ReturnConnection(std::move(_connect));
  return true;
}

bool RedisMgr::ExistKey(const std::string &key) {
  auto _connect = _pool->GetConnection();
  if (_connect == nullptr) {
    return false;
  }
  auto reply =
      (redisReply *)redisCommand(_connect.get(), "EXISTS %s", key.c_str());
  if (reply == nullptr) {
    std::cerr << "reply is null, command [ EXISTS " << key
              << " ] execute failed!" << std::endl;
    return false;
  }
  if (reply->type != REDIS_REPLY_INTEGER) {
    std::cout << "Execut command [ EXISTS " << key << " ] failure!"
              << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  if (reply->integer == 0) {
    std::cout << "Execut command [ EXISTS " << key << " ] failure!"
              << std::endl;
    freeReplyObject(reply);
    _pool->ReturnConnection(std::move(_connect));
    return false;
  }
  std::cout << "Execut command [ EXISTS " << key << " ] success!" << std::endl;
  _pool->ReturnConnection(std::move(_connect));
  freeReplyObject(reply);
  return true;
}

void RedisMgr::Close() { _pool->Close(); }

RedisMgr ::~RedisMgr() { Close(); }